package userInterface;

import java.awt.Color;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

/**
 * Objects of this class contain information about a list of drawing objects.  The objects are
 * arranged in sequential order that they should be drawn in.
 */
public class DrawingModel 
{
	/**
	 * Creates an empty drawing model that has a unique default file name and no contents
	 */
	public DrawingModel ()
	{
		_objects = new ArrayList<DrawObject>();
		_unsavedChanges = true;
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyMMddHHmmss");
		_filename = "file-" + simpleDateFormat.format( new Date() ) + ".txt";
		_views = new ArrayList<UpdateableView>();
	}
	
	/**
	 * Reads the specific file and creates a new DrawingModel object that contains all of the 
	 * information in the file. If there is no such file then an exception should be thrown.
	 * @param fileName the name of the file to be read.
	 */
	public DrawingModel(String fileName)
	{
		_objects = FileReader465.readFile(fileName);				

		for (DrawObject o : _objects)
			o.addModelListener(this);
	
		_filename = fileName;
		_unsavedChanges = false;
		_views = new ArrayList<UpdateableView>();
	}
	
	/**
	 * Returns the name of the file associated with this model.
	 */
	public String getFileName()
	{
		return _filename;
	}
	
	/**
	 * Changes the file name associated with this model
	 * @param newFileName the new file name
	 */
	public void setFileName(String newFileName)
	{
		_filename = newFileName;
	}
	
	/**
	 * Saves the contents of this model to its file.
	 */
	public void save()
	{
		System.out.println(_filename);
		FileWriter465.writeFile(_filename, _objects);
		_unsavedChanges = false;
	}
	
	public void open(String filename)
	{
		_filename = filename;
		_objects = FileReader465.readFile(_filename);
		_unsavedChanges = false;

		for (DrawObject o : _objects)
			o.addModelListener(this);

		modelChanged();
	}
	
	/**
	 * Returns true if there are unsaved changes.
	 */
	public boolean unsavedChanges()
	{
		return _unsavedChanges;
	}
	
	/**
	 * Adds the specified DrawObject to the end of the list of drawable objects
	 * @param newDrawObject
	 */
	public void addDrawObject(DrawObject newDrawObject)
	{
		newDrawObject.addModelListener(this);
		_objects.add(newDrawObject);
		_unsavedChanges = true;
		modelChanged();
	}
	
	/**
	 * Returns the number of drawable objects in this model.
	 */
	public int nDrawObjects()
	{
		return _objects.size();
	}
	
	/**
	 * Returns the specified DrawObject. Indexes begin at zero.
	 * @param i index of the desired object. Must be less than nDrawObjects()
	 */
	public DrawObject getDrawObject(int i)
	{
		return _objects.get(i);
	}
	
	/**
	 * Removes the specified object from the list of drawable objects.
	 * @param i the index of the object to be removed.
	 */
	public void removeDrawObject(int i)
	{
		_objects.remove(i);
		modelChanged();
	}
	
	public void addViewListener(DrawingView view)
	{
		_views.add(view);
	}
	
	public void modelChanged()
	{
		for (UpdateableView v : _views)
			v.update();
	}
	
	/**
	* This method is a regression test to verify that this class is
	* implemented correctly. It should test all of the methods including
	* the exceptions. It should be completely self checking. This 
	* should write "testing DrawingModel" to System.out before it
	* starts and "DrawingModel OK" to System.out when the test
	* terminates correctly. Nothing else should appear on a correct
	* test. Other messages should report any errors discovered.
	**/
	public static void Test()
	{
		System.out.println("testing DrawingModel");
		
		if (TestGetFileName() &&
				TestSetFileName() &&
				TestSave() &&
				TestUnsavedChanges() &&
				TestAddDrawObject() &&
				TestNDrawObjects() &&
				TestGetDrawObject() &&
				TestRemoveDrawObject())
		{
			System.out.println("DrawingModel OK");
		}
	}
	
	private static boolean TestGetFileName()
	{
		return true;
	}

	private static boolean TestSetFileName()
	{
		return true;
	}

	private static boolean TestSave()
	{
		DrawingModel m = new DrawingModel();
		
		m.addDrawObject(new Line(new Point(13, 13), new Point(13, 13), 13, new Color(1, 2, 3)));
		
		m.save();
		
		DrawingModel m1 = new DrawingModel(m.getFileName());
		
		DrawObject d = m1.getDrawObject(0);
		if (d.getColor().getRed() != 1 || d.getColor().getGreen() != 2 || d.getColor().getBlue() != 3)
		{
			System.out.println("Saveing a file or loading a new file error "
					+ d.getColor().getRed() + " " + d.getColor().getGreen() + " " + d.getColor().getBlue());
			return false;
		}
		
		return true;
	}

	private static boolean TestUnsavedChanges()
	{
		DrawingModel m = new DrawingModel();
		
		if (!m.unsavedChanges())
		{
			System.out.println("you should now have unsaved changes");
			return false;
		}
		
		m.save();
		
		if (m.unsavedChanges())
		{
			System.out.println("you should have no unsaved changes");
			return false;
		}
		
		m.addDrawObject(new Line(new Point(0, 0), new Point(5, 5), 1, new Color(0, 0, 0)));
		
		if (!m.unsavedChanges())
		{
			System.out.println("You should have unsaved changes");
			return false;
		}
		
		return true;
	}

	private static boolean TestAddDrawObject()
	{
		DrawingModel m = new DrawingModel();
		Line line = new Line(new Point(1, 1), new Point(2, 2), 1, new Color(1, 2, 3));
		m.addDrawObject(line);
	
		if (m.nDrawObjects() != 1)
		{
			System.out.println("number of objects and nDrawObjects() are inconsistant");
			return false;
		}
		
		
		if (line != m.getDrawObject(0))
		{
			System.out.println("added a draw object and it is now null");
			return false;
		}
		
		return true;
	}

	private static boolean TestNDrawObjects()
	{
		DrawingModel m = new DrawingModel();
		
		if (m.nDrawObjects() != 0)
		{
			System.out.println("model not initalized to have zero objects");
			return false;
		}
		
		m.addDrawObject(new Line(new Point(0, 0), new Point(5, 5), 1, new Color(1, 1, 1)));
		
		if (m.nDrawObjects() != 1)
		{
			System.out.println("model should have only one draw object but has " + m.nDrawObjects());
			return false;
		}
		
		for (int i = 0; i < 9; i++)
		{
			m.addDrawObject(new Line(new Point(0, 0), new Point(5, 5), 1, new Color(0, 0, 0)));
		}
		
		if (m.nDrawObjects() != 10)
		{
			System.out.println("model should have ten items but has " + m.nDrawObjects());
			return false;
		}
		
		for (int i = 9; i >= 0; i--)
		{
			m.removeDrawObject(0);
		}
		
		if (m.nDrawObjects() != 0)
		{
			System.out.println("model should have zero items but has " + m.nDrawObjects());
			return false;
		}
		
		return true;
	}

	private static boolean TestGetDrawObject()
	{
		DrawingModel m = new DrawingModel();
		
		DrawObject line = new Line(new Point(0, 0), new Point(0, 0), 1, new Color(0, 0, 0));
		DrawObject rect = new Rectangle(new Point(0, 0), 0, 0, 0, new Color(0, 0, 0));
		DrawObject elip = new Ellipse(new Point(0, 0), 0, 0, 0, new Color(0, 0, 0));
		
		m.addDrawObject(line);
		m.addDrawObject(rect);
		m.addDrawObject(elip);
		
		if (m.getDrawObject(0) != line)
		{
			System.out.println("did not properly get the line");
			return false;
		}
		
		if (m.getDrawObject(1) != rect)
		{
			System.out.println("did not properly get the rect");
			return false;
		}
		
		if (m.getDrawObject(2) != elip)
		{
			System.out.println("did not properly get the ellipse");
			return false;
		}
		
		return true;
	}

	private static boolean TestRemoveDrawObject()
	{
		DrawingModel m = new DrawingModel();
		
		for (int i = 0; i < 10; i++)
		{
			m.addDrawObject(new Line(new Point(0, 0), new Point(5, 5), 1, new Color(0, 0, 0)));
		}
		
		for (int i = 0; i < m.nDrawObjects();)
		{
			m.removeDrawObject(i);
		}
		
		if (m.nDrawObjects() != 0)
		{
			System.out.println("did not properly remove items still have " + m.nDrawObjects() + " items");
			return false;
		}
		
		return true;
	}

	private ArrayList<DrawObject> _objects;
	
	private String _filename;

	private boolean _unsavedChanges;
	
	private ArrayList<UpdateableView> _views;
}
